/**
 * @file dynArray.c
 * @brief Implementa le funzioni per una struttura di Array Dinamica.
 *
 * Complete description of this file.
 *
 * @date Mar 16, 2012
 * @author Francesco Corvino
 */
#include "dynArray.h"

#define GETELEMENT(arr,i) ((arr)->elemBuff + ( (i) * ((arr)->sizeSingleElem)))

/**
 * @brief Inizializza l'array dinamico.
 *
 * Inizializza una struttura di array dinamico.
 *
 * L'array è inizializzato con la funzione destroy NULL, per specificare una funzione che
 * si occupa di rimuovere dalla memoria i singoli elementi dell'array inizializzare successivamente
 * al richiamo di questa funzione il campo destroy.
 *
 * @param arr struttura da inizializzare.
 * @param matchElem funzione che permette di ordinare gli elementi dell'array
 * @param sizeElem dimensione fissa dei singoli elementi
 */
void DynArray_init(DynArray * arr, int (*matchElem)(const void*, const void*), size_t sizeElem){
	arr->destroy = NULL;
	arr->cmpElem = matchElem;
	arr->elemBuff = NULL;
	arr->sizeSingleElem = sizeElem;
	arr->numelement = 0;
}

/**
 * @brief Si occupa di aggiungere un elemento all'array dinamico.
 *
 * @see DynArray struttura che definisce un'array dinamico.
 * @see removeElementFromDynArray() funzione che rimuove un elemento dall'array.
 */
int DynArray_add(DynArray * arr, void * elem){
	int numToAlloc = arr->numelement + 1;
	void *_tmp = realloc(arr->elemBuff,
			(numToAlloc * arr->sizeSingleElem));
	if (!_tmp) return -1;
	arr->elemBuff = _tmp; // Things are looking good so far
	memcpy( arr->elemBuff + (arr->numelement * arr->sizeSingleElem),
			elem, arr->sizeSingleElem);
	arr->numelement++;
	if(arr->cmpElem!=NULL){
		qsort(arr->elemBuff,
				arr->numelement,arr->sizeSingleElem,
				arr->cmpElem);
	}
	return 0;
}

int DynArray_remove(DynArray * arr, void * elem){
	// è necessario che sia fornito un metodo per confrontare l'elemento da eliminare
	if(arr->cmpElem==NULL) return -1;
	int i; char isDeletedElem = 0;
	for(i=0;i<arr->numelement;i++){
		void *delel = GETELEMENT(arr,i); //(arr->elemBuff + (i * arr->sizeSingleElem));
		// controlla che sia l'elemento giusto
		if((arr->cmpElem(elem,delel))!=0) continue;
		isDeletedElem = 1;
		int iLast = arr->numelement -1;
		if(i!=iLast) { // se è l'ultimo elemento non è necessario eseguire operazioni
			void *ptrLast = GETELEMENT(arr,iLast);
			memcpy(delel,ptrLast,arr->sizeSingleElem);
		}
		size_t newsize = arr->sizeSingleElem * iLast;
		if(newsize==0){
			free(arr->elemBuff);
			arr->elemBuff = NULL;
		} else {
			void *tmp = realloc(arr->elemBuff,newsize);
			if(tmp!=NULL) arr->elemBuff = tmp;
		}
	}
	if(isDeletedElem) arr->numelement--;
	return arr->numelement;
}

void * DynArray_get(DynArray * arr, const void * index){
	if(arr->cmpElem!=NULL){ // utilizza la funzione di confronto
		return bsearch(index,arr->elemBuff,
				arr->numelement,arr->sizeSingleElem,
				arr->cmpElem);
	} else { // cerca di convertire in intero l'argomento
		return DynArray_getIndex(arr, (int) &(index));
	}
}

void * DynArray_getIndex(DynArray * arr, const int index){
	if(index > arr->numelement) return NULL;
	else {
		return GETELEMENT(arr,index);
	}
}

/**
 * @brief elimina la memoria allocata per un'array dinamico cancellandone tutti gli elementi.
 */
void DynArray_destroy(DynArray * arr){
	int i;
	if(arr->destroy!=NULL){
		for(i=0;i<arr->numelement;i++){
			void *delel = GETELEMENT(arr,i);
			(*(arr->destroy))(delel);
		}
	}
	free(arr->elemBuff);
	arr->elemBuff = NULL;
	arr->numelement = 0;
}

void * DynArray_getNext(DynArray* arr, int * index){
	void * tmp = NULL;
	if(arr->numelement <= (*index)) return NULL;
	tmp = GETELEMENT(arr,(*index));
	(*index)++;
	return tmp;
}
